<?php

namespace App\Common\Middleware;

use App\Common\Constants\ErrorCode;
use App\Common\Model\SystemLogModel;
use App\Common\Service\Arr;
use Hyperf\Redis\RedisFactory;
use Hyperf\Utils\ApplicationContext;
use Psr\Http\Server\MiddlewareInterface;
use App\User\Service\UserService;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface as HttpResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

class AesMiddleware implements  MiddlewareInterface
{

    public $resourceRedis;
    /**
     * @Inject
     * @var ConfigInterface
     */
    protected $config;

    /**
     * @Inject
     * @var UserService
     */
    protected $userService;

    /**
     * @Inject
     * @var RequestInterface
     */
    protected $requestion;


    /**
     * @var HttpResponse
     */
    protected $response;

    /**
     * @Inject
     * @var Arr
     */
    private $arr;

    private static $aes_key = 'ljda_dj@.dv?ylxp';

    /**
     * The URIs that should be excluded from key verification.
     * 201225 AES加密验证排除
     * @var array
     * @author zhangzhiyuan
     */
    protected $exceptUrl = [

    ];

    public function __construct(HttpResponse $response)
    {
            $container = ApplicationContext::getContainer();
            $this->container = $container;
            $this->response = $response;
            $this->resourceRedis = $container->get(RedisFactory::class)->get('resource');
    }

    /**
     * @param ServerRequestInterface $request
     * @param RequestHandlerInterface $handler
     * @return ResponseInterface
     * @author zhanghziyuan
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
            //platformConfidential 平台机密
            //platformConfidentialSource 平台机密来源 1、小程序、2、web 3、商家App
            $uri = $request->getUri()->getPath();
            if (array_key_exists($uri, array_flip($this->exceptUrl))) return $handler->handle($request);
            $isValid = false;
            $method =$request->getMethod(); //传输方式
            switch ($method){
                case 'GET':
                    $getQueryParamsArr =$request->getQueryParams();
                    break;
                case 'PUT':
                    $getParams =$request->getBody()->getContents();
                    parse_str($getParams,$getQueryParamsArr);
                    break;
                case 'POST':
                    $getParams =$request->getBody()->getContents();
                    parse_str($getParams,$getQueryParamsArr);
                    break;
                case 'DELETE':
                    $getParams =$request->getBody()->getContents();
                    parse_str($getParams,$getQueryParamsArr);
                    break;
            }
            $pKey = $request->getHeaderLine('pKey');
            if (env('OPEN_AES',false) == true) {
                if($this->checkAesConfidential($getQueryParamsArr) === true){
                    $isValid=true;
                }else{
                    return $this->checkAesConfidential($getQueryParamsArr);
                }
            }
            if(env('OPEN_KEY',false) == true){
                if($this->checkHeaderpKey($pKey) === true){
                    $isValid=true;
                }else{
                    return $this->checkHeaderpKey($pKey);
                }
            }
            //解密
            if($isValid){
                return $handler->handle($request);
            }
            return $handler->handle($request);
    }

    /**
     * Aes加密认证校验
     * @param $getQueryParamsArr
     * @return bool|ResponseInterface
     */
    private function checkAesConfidential($getQueryParamsArr)
    {
        $decryptString=$this->getSignContent($getQueryParamsArr);
        $encryptionStr = base64_encode(openssl_encrypt($decryptString, 'AES-128-ECB', self::$aes_key, OPENSSL_RAW_DATA));
        if(empty($getQueryParamsArr['platformConfidential'])){
                $data = [
                    'code' =>ErrorCode::AUTH_OPEN_ASE_ERROR,
                    'msg' =>ErrorCode::getMessage(ErrorCode::AUTH_OPEN_ASE_ERROR),
                    'data' => [],
                ];
                return $this->response->json($data);
        }
        $decrypted = openssl_decrypt(base64_decode($getQueryParamsArr['platformConfidential']?? ''), 'AES-128-ECB', self::$aes_key, OPENSSL_RAW_DATA);
        $content ='后端加密字符串'.$decryptString.'后端加密后'.$encryptionStr.' 前端加密:'.$getQueryParamsArr['platformConfidential'].'前端解密字符串'. $decrypted;
        $this->write('加密字符串', $content);
        if($encryptionStr != $getQueryParamsArr['platformConfidential']){
                $data = [
                    'code' =>ErrorCode::AUTH_ASE_INVALID_ERROR,
                    'msg' =>ErrorCode::getMessage(ErrorCode::AUTH_ASE_INVALID_ERROR),
                    'data' => [],
                ];
                return $this->response->json($data);
        }
        return true;
    }

    /**
     * Header加密Pkey认证校验
     * @param $pKey
     * @return bool|ResponseInterface
     */
    private function checkHeaderpKey($pKey)
    {
        if(empty($pKey)){
            $data = [
                'code' =>ErrorCode::HEADER_KEY_ERROR,
                'msg' =>ErrorCode::getMessage(ErrorCode::HEADER_KEY_ERROR),
                'data' => [],
            ];
            return $this->response->json($data);
        }
        $isExist =$this -> resourceRedis ->exists('pKey:'.$pKey);
        if($isExist){
            $data = [
                'code' =>ErrorCode::HEADER_KEY_INVALID_ERROR,
                'msg' =>ErrorCode::getMessage(ErrorCode::HEADER_KEY_INVALID_ERROR),
                'data' => [],
            ];
            return $this->response->json($data);
        }else{
            $this -> resourceRedis ->set('pKey:'.$pKey,'1',5);
            return true;
        }
    }
    /**
     * @param $params
     * @return string
     */
    private function getSignContent($params)
    {
        if (isset($params['platformConfidential'])) {
            unset($params['platformConfidential']);
        }
        ksort($params);
        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if ("@" != substr($v, 0, 1)) {
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }
        return $stringToBeSigned;
    }

    /**
     * 写入系统日志
     * @param string $action
     * @param string $content
     */
    public function write(string $action, string $content)
    {
        $data = [
            'node' => 'AesMiddleware',
            'action' => $action,
            'content' => $content,
            'geoip' => '127.0.0.1',
            'username' => '',
            'create_at' => date('Y-m-d H:i:s')
        ];
        SystemLogModel::query()->create($data);
    }

}